package com.twb.core.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;

/**
 * @ClassName BeanUtils
 * @Description 扩展spring的BeanUtils，增加拷贝属性排除null值的功能(注：String为null不考虑)
 * 
 * @author DHL
 * @date 2013-12-29 下午4:15:37
 */
public class BeanUtils extends org.springframework.beans.BeanUtils {

	/**
	 * @Title isNullOrEmpty
	 * @Class BeanUtils.java
	 * @Description 判断对象或对象数组中每一个对象是否为空: 对象为null，字符序列长度为0，集合类、Map为empty
	 * 
	 * @param obj
	 * @return
	 * 
	 * @author DHL
	 * @date 2016年4月25日 下午12:14:35
	 */
	public static boolean isNullOrEmpty(Object obj) {
		if (obj == null)
			return true;

		if (obj instanceof CharSequence)
			return ((CharSequence) obj).length() == 0;

		if (obj instanceof Collection)
			return ((Collection<?>) obj).isEmpty();

		if (obj instanceof Map)
			return ((Map<?, ?>) obj).isEmpty();

		if (obj instanceof Object[]) {
			Object[] object = (Object[]) obj;
			if (object.length == 0) {
				return true;
			}
			boolean empty = true;
			for (int i = 0; i < object.length; i++) {
				if (!isNullOrEmpty(object[i])) {
					empty = false;
					break;
				}
			}
			return empty;
		}
		return false;
	}
	
	
	/**
	 * @Title isNotEmpty
	 * @Description 判断对象或对象数组中每一个对象是否不为空
	 * @param obj
	 * @return
	 * @author DHL
	 * @date 2021年3月8日 下午1:23:50
	 */
	public static boolean isNotEmpty(Object obj) {
		return !isNullOrEmpty(obj);
	}

	/**
	 * @Title copyNotNullProperties
	 * @Class BeanUtils.java
	 * @Description 拷贝属性排除null值(注：String为null不考虑)
	 * 
	 * @param source 源对象
	 * @param target 目标对象
	 * @throws BeansException
	 * 
	 * @author DHL
	 * @date 2016年4月25日 上午11:36:03
	 */
	public static void copyNotNullProperties(Object source, Object target) throws BeansException {
		copyNotNullProperties(source, target, null, null);
	}
	
	/**
	 * @Title copyNotNullProperties
	 * @Description 拷贝属性排除null值(注：String为null不考虑)
	 * @param <T>
	 * @param source	源对象
	 * @param target	目标对象
	 * @throws BeansException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @author DHL
	 * @date 2020年12月30日 下午2:43:50
	 */
	public static <T> T copyNotNullProperties(Object source, Class<T> target) throws BeansException, InstantiationException, IllegalAccessException {
		T newInstance = target.newInstance();
		
		copyNotNullProperties(source, newInstance, null, null);
		
		return newInstance;
	}

	/**
	 * @Title copyNotNullProperties
	 * @Class BeanUtils.java
	 * @Description
	 * 
	 * @param source           源对象
	 * @param target           目标对象
	 * @param ignoreProperties 不需要拷贝的属性集合
	 * @throws BeansException
	 * 
	 * @author DHL
	 * @date 2016年4月25日 上午11:38:47
	 */
	public static void copyNotNullProperties(Object source, Object target, String[] ignoreProperties) throws BeansException {
		copyNotNullProperties(source, target, null, ignoreProperties);
	}

	/**
	 * @Title copyNotNullProperties
	 * @Class BeanUtils.java
	 * @Description
	 * 
	 * @param source   源对象
	 * @param target   目标对象
	 * @param editable 验证源对象与目标对象是否为同一个Bean对象
	 * @throws BeansException
	 * 
	 * @author DHL
	 * @date 2016年4月25日 上午11:36:58
	 */
	public static void copyNotNullProperties(Object source, Object target, Class<?> editable) throws BeansException {
		copyNotNullProperties(source, target, editable, null);
	}

	/**
	 * @Title copyNotNullProperties
	 * @Class BeanUtils.java
	 * @Description 拷贝属性排除null值(注：String为null不考虑)
	 * 
	 * @param source           源对象
	 * @param target           目标对象
	 * @param editable         验证源对象与目标对象是否为同一个Bean对象
	 * @param ignoreProperties 不需要拷贝的属性集合
	 * @throws BeansException
	 * 
	 * @author DHL
	 * @date 2016年4月25日 上午11:10:42
	 */
	public static void copyNotNullProperties(Object source, Object target, Class<?> editable, String[] ignoreProperties) throws BeansException {

		Assert.notNull(source, "Source must not be null");
		Assert.notNull(target, "Target must not be null");

		Class<?> actualEditable = target.getClass();
		if (editable != null) {
			if (!editable.isInstance(target)) {
				throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");
			}
			actualEditable = editable;
		}
		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
		List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

		// 深拷贝
		//Object source = JSON.parseObject(JSON.toJSONString(sourceObject), sourceObject.getClass());

		for (PropertyDescriptor targetPd : targetPds) {
			Method writeMethod = targetPd.getWriteMethod();

			if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
				PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
				if (sourcePd != null) {
					Method readMethod = sourcePd.getReadMethod();

					if (readMethod != null) {

						try {
							if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
								readMethod.setAccessible(true);
							}

							Object value = readMethod.invoke(source);

							// 这里判断以下value是否为空，当然这里也能进行一些特殊要求的处理
							// 例如绑定时格式转换等等，如果是String类型，则不需要验证是否为空
							if (value != null || readMethod.getReturnType().getName().equals("java.lang.String")) {
								boolean isEmpty = false;

								isEmpty = isNullOrEmpty(value);

								if (!isEmpty) {
									if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
										writeMethod.setAccessible(true);
									}
									writeMethod.invoke(target, value);
								}
							}

						} catch (Exception ex) {
							throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", ex);
						}
					}
				}

			}
		}

	}

	/**
	 * 判断一个对象中,引用类型的属性字段,是否全部由null组成
	 * 
	 * @param object
	 * @return
	 */
	public static boolean checkObjectConsistWithNull(Object obj) {

		if (obj == null) {
			return true;
		}

		Field[] fields = obj.getClass().getDeclaredFields();
		for (Field field : fields) {
			if (!field.getType().isPrimitive()) {
				try {
					PropertyDescriptor fieldDescriptor = getPropertyDescriptor(obj.getClass(), field.getName());
					if (fieldDescriptor.getReadMethod().invoke(obj) != null) {
						return false;
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

		return true;
	}

	/**
	 * 将一个 Map 对象转化为一个 JavaBean
	 * 
	 * @param type 要转化的类型
	 * @param map  包含属性值的 map
	 * @return 转化出来的 JavaBean 对象
	 * @throws IntrospectionException    如果分析类属性失败
	 * @throws IllegalAccessException    如果实例化 JavaBean 失败
	 * @throws InstantiationException    如果实例化 JavaBean 失败
	 * @throws InvocationTargetException 如果调用属性的 setter 方法失败
	 */
	public static <T> T map2Bean(Class<T> type, Map<String, String> map) throws IntrospectionException, IllegalAccessException, InstantiationException, InvocationTargetException {
		BeanInfo beanInfo = Introspector.getBeanInfo(type); // 获取类属性
		T obj = type.newInstance(); // 创建 JavaBean 对象

		// 给 JavaBean 对象的属性赋值
		PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
		for (int i = 0; i < propertyDescriptors.length; i++) {
			PropertyDescriptor descriptor = propertyDescriptors[i];
			String propertyName = descriptor.getName().toUpperCase();

			if (map.containsKey(propertyName)) {
				// 下面一句可以 try 起来，这样当一个属性赋值失败的时候就不会影响其他属性赋值。
				Object value = map.get(propertyName);

				Object[] args = new Object[1];
				args[0] = value;

				descriptor.getWriteMethod().invoke(obj, args);
			}
		}
		return obj;
	}

	/**
	 * 将一个 JavaBean 对象转化为一个 Map
	 * 
	 * @param bean 要转化的JavaBean 对象
	 * @return 转化出来的 Map 对象
	 * @throws IntrospectionException    如果分析类属性失败
	 * @throws IllegalAccessException    如果实例化 JavaBean 失败
	 * @throws InvocationTargetException 如果调用属性的 setter 方法失败
	 */
	public static Map<String, Object> bean2Map(Object bean) throws IntrospectionException, IllegalAccessException, InvocationTargetException {
		Class<?> type = bean.getClass();
		Map<String, Object> returnMap = new HashMap<String, Object>();
		BeanInfo beanInfo = Introspector.getBeanInfo(type);

		PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
		for (int i = 0; i < propertyDescriptors.length; i++) {
			PropertyDescriptor descriptor = propertyDescriptors[i];
			String propertyName = descriptor.getName();
			if (!propertyName.equals("class")) {
				Method readMethod = descriptor.getReadMethod();
				Object result = readMethod.invoke(bean, new Object[0]);
				if (result != null) {
					returnMap.put(propertyName, result);
				} else {
					returnMap.put(propertyName, "");
				}
			}
		}
		return returnMap;
	}

	/**
	 * @Title listMap2ListBean
	 * @Class BeanUtil.java
	 * @Package com.dths.web.servlet.utils
	 * 
	 * @param type
	 * @param thelist
	 * @return
	 * @throws IntrospectionException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws InvocationTargetException
	 * 
	 * @author DSY
	 * @date 2014年11月28日 上午11:23:19
	 */
	public static <T> List<T> listMap2ListBean(Class<T> type, List<Map<String, String>> thelist) throws IntrospectionException, IllegalAccessException, InstantiationException, InvocationTargetException {
		List<T> returnList = new ArrayList<T>();
		for (Map<String, String> map : thelist) {
			T bean = map2Bean(type, map);
			returnList.add(bean);
		}
		return returnList;
	}
	
	/**
	 * @Title copyList
	 * @Description list 拷贝
	 * @param <T>
	 * @param <E>
	 * @param type
	 * @param sourceList
	 * @return
	 * @throws BeansException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @author DHL
	 * @date 2020年12月31日 上午10:36:39
	 */
	public static <T, E> List<T> copyList(Class<T> type, List<E> sourceList) throws BeansException, InstantiationException, IllegalAccessException{
		List<T> returnList = new ArrayList<T>();
		
		for (E source : sourceList) {
			T target = copyNotNullProperties(source, type);
			
			returnList.add(target);
		}
		
		return returnList;
	}

	public static void main(String[] args) {

	}

}
